package com.codegenerator.core;

import com.codegenerator.config.GeneratorConfig;
import com.codegenerator.util.DatabaseUtils;
import com.codegenerator.util.FileUtils;
import com.codegenerator.util.FreemarkerUtils;
import com.codegenerator.util.JsonUtils;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器核心类
 */
public class CodeGenerator {
    private static final String CONFIG_FILE = "generator-config.json";
    private GeneratorConfig generatorConfig;
    
    public void generate() throws IOException, TemplateException, SQLException {
        // 1. 读取配置文件
        loadConfig();
        
        // 2. 如果配置中没有指定表结构，从数据库获取
        fetchTableInfoFromDatabase();
        
        // 3. 生成代码
        generateCode();
    }
    
    private void loadConfig() throws IOException {
        // 读取JSON配置文件
        generatorConfig = JsonUtils.fromJsonFile(CONFIG_FILE, GeneratorConfig.class);
        if (generatorConfig == null) {
            throw new RuntimeException("配置文件不存在或格式错误: " + CONFIG_FILE);
        }
        
        // 初始化默认值
        initDefaultConfig();
    }
    
    private void initDefaultConfig() {
        GeneratorConfig.PackageConfig packageConfig = generatorConfig.getPackageConfig();
        if (packageConfig == null) {
            packageConfig = new GeneratorConfig.PackageConfig();
            generatorConfig.setPackageConfig(packageConfig);
        }
        
        // 设置默认包名
        if (StringUtils.isBlank(packageConfig.getEntity())) {
            packageConfig.setEntity("entity");
        }
        if (StringUtils.isBlank(packageConfig.getMapper())) {
            packageConfig.setMapper("mapper");
        }
        if (StringUtils.isBlank(packageConfig.getManager())) {
            packageConfig.setManager("manager");
        }
        if (StringUtils.isBlank(packageConfig.getService())) {
            packageConfig.setService("service");
        }
        if (StringUtils.isBlank(packageConfig.getServiceImpl())) {
            packageConfig.setServiceImpl("service.impl");
        }
        if (StringUtils.isBlank(packageConfig.getModel())) {
            packageConfig.setModel("model");
        }
        if (StringUtils.isBlank(packageConfig.getMapstruct())) {
            packageConfig.setMapstruct("mapper");
        }
        
        // 设置默认路径
        GeneratorConfig.PathConfig pathConfig = generatorConfig.getPathConfig();
        if (pathConfig == null) {
            pathConfig = new GeneratorConfig.PathConfig();
            generatorConfig.setPathConfig(pathConfig);
        }
        
        if (StringUtils.isBlank(pathConfig.getBaseDir())) {
            pathConfig.setBaseDir(System.getProperty("user.dir"));
        }
        if (StringUtils.isBlank(pathConfig.getJavaDir())) {
            pathConfig.setJavaDir(pathConfig.getBaseDir() + "/src/main/java");
        }
        if (StringUtils.isBlank(pathConfig.getResourcesDir())) {
            pathConfig.setResourcesDir(pathConfig.getBaseDir() + "/src/main/resources");
        }
    }
    
    private void fetchTableInfoFromDatabase() throws SQLException {
        // 如果配置中没有指定表结构，则从数据库获取

        for (GeneratorConfig.TableConfig table : generatorConfig.getTables()){
            GeneratorConfig.TableConfig dbTableInfo = DatabaseUtils.getSingleTableInfo(
                    generatorConfig.getDatabase().getUrl(),
                    generatorConfig.getDatabase().getUsername(),
                    generatorConfig.getDatabase().getPassword(),
                    generatorConfig.getDatabase().getDriverClassName(),
                    table.getTableName()
                    );
            table.setComments(dbTableInfo.getComments());
            table.setColumns(dbTableInfo.getColumns());
        }

    }
    
    private void generateCode() throws IOException, TemplateException {
        GeneratorConfig.PathConfig pathConfig = generatorConfig.getPathConfig();
        GeneratorConfig.PackageConfig packageConfig = generatorConfig.getPackageConfig();
        
        // 遍历所有表生成代码
        for (GeneratorConfig.TableConfig table : generatorConfig.getTables()) {
            String tableName = table.getTableName();
            String domainObjectName = table.getDomainObjectName();
            
            if (StringUtils.isBlank(domainObjectName)) {
                // 如果没有指定实体类名，则将表名转为驼峰命名法作为实体类名
                domainObjectName = FileUtils.underlineToCamel(tableName, true);
                table.setDomainObjectName(domainObjectName);
            }
            
            System.out.println("开始生成表 " + tableName + " 的代码，实体类名: " + domainObjectName);
            
            // 准备模板数据
            Map<String, Object> dataModel = prepareDataModel(table);
            
            // 生成实体类
            generateEntity(dataModel, pathConfig, packageConfig);
            
            // 生成Mapper接口
            generateMapper(dataModel, pathConfig, packageConfig);
            
            // 生成Manager层
            generateManager(dataModel, pathConfig, packageConfig);
            
            // 生成Service接口
            generateService(dataModel, pathConfig, packageConfig);
            
            // 生成Service实现类
            generateServiceImpl(dataModel, pathConfig, packageConfig);
            
            // 生成DTO
            generateDTO(dataModel, pathConfig, packageConfig);
            
            // 生成MapStruct映射器
            generateMapStruct(dataModel, pathConfig, packageConfig);
            
            System.out.println("表 " + tableName + " 的代码生成完成");
        }
    }
    
    private Map<String, Object> prepareDataModel(GeneratorConfig.TableConfig table) {
        Map<String, Object> dataModel = new HashMap<>();
        GeneratorConfig.PackageConfig packageConfig = generatorConfig.getPackageConfig();
        
        // 基础包名
        String basePackage = packageConfig.getParent();
        
        // 设置包名
        dataModel.put("basePackage", basePackage);
        dataModel.put("entityPackage", basePackage + "." + packageConfig.getEntity());
        dataModel.put("mapperPackage", basePackage + "." + packageConfig.getMapper());
        dataModel.put("managerPackage", basePackage + "." + packageConfig.getManager());
        dataModel.put("servicePackage", basePackage + "." + packageConfig.getService());
        dataModel.put("serviceImplPackage", basePackage + "." + packageConfig.getServiceImpl());
        dataModel.put("modelPackage", basePackage + "." + packageConfig.getModel());
        dataModel.put("mapstructPackage", basePackage + "." + packageConfig.getMapstruct());
        
        // 设置表信息
        dataModel.put("tableName", table.getTableName());
        dataModel.put("tableComment", table.getComments());
        dataModel.put("primaryKeyJavaType", table.getPrimaryKeyJavaType());
        dataModel.put("domainObjectName", table.getDomainObjectName());
        dataModel.put("columns", table.getColumns());
        
        // 设置作者信息
        dataModel.put("author", generatorConfig.getAuthor());
        
        // 设置当前日期
        dataModel.put("date", new java.util.Date());
        
        return dataModel;
    }
    
    private void generateEntity(Map<String, Object> dataModel, 
                               GeneratorConfig.PathConfig pathConfig,
                               GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String entityPackage = (String) dataModel.get("entityPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + ".java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                entityPackage, 
                fileName
        );
        
        generateFile("templates/entity.ftl", outputPath, dataModel);
    }
    
    private void generateMapper(Map<String, Object> dataModel, 
                               GeneratorConfig.PathConfig pathConfig,
                               GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String mapperPackage = (String) dataModel.get("mapperPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "Mapper.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                mapperPackage, 
                fileName
        );
        
        generateFile("templates/mapper.ftl", outputPath, dataModel);
    }
    
    private void generateManager(Map<String, Object> dataModel, 
                                GeneratorConfig.PathConfig pathConfig,
                                GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String managerPackage = (String) dataModel.get("managerPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "Manager.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                managerPackage, 
                fileName
        );
        
        generateFile("templates/manager.ftl", outputPath, dataModel);
    }
    
    private void generateService(Map<String, Object> dataModel, 
                                GeneratorConfig.PathConfig pathConfig,
                                GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String servicePackage = (String) dataModel.get("servicePackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "Service.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                servicePackage, 
                fileName
        );
        
        generateFile("templates/service.ftl", outputPath, dataModel);
    }
    
    private void generateServiceImpl(Map<String, Object> dataModel, 
                                    GeneratorConfig.PathConfig pathConfig,
                                    GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String serviceImplPackage = (String) dataModel.get("serviceImplPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "ServiceImpl.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                serviceImplPackage, 
                fileName
        );
        
        generateFile("templates/serviceImpl.ftl", outputPath, dataModel);
    }
    
    private void generateDTO(Map<String, Object> dataModel, 
                            GeneratorConfig.PathConfig pathConfig,
                            GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String modelPackage = (String) dataModel.get("modelPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "DTO.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                modelPackage, 
                fileName
        );
        
        generateFile("templates/dto.ftl", outputPath, dataModel);
    }
    
    private void generateMapStruct(Map<String, Object> dataModel, 
                                  GeneratorConfig.PathConfig pathConfig,
                                  GeneratorConfig.PackageConfig packageConfig) throws IOException, TemplateException {
        String mapstructPackage = (String) dataModel.get("mapstructPackage");
        String domainObjectName = (String) dataModel.get("domainObjectName");
        String fileName = domainObjectName + "Mapper.java";
        
        String outputPath = FileUtils.getOutputFilePath(
                pathConfig.getJavaDir(), 
                mapstructPackage, 
                fileName
        );
        
        generateFile("templates/mapstruct.ftl", outputPath, dataModel);
    }
    
    private void generateFile(String templateName, String outputPath, Map<String, Object> dataModel) 
            throws IOException, TemplateException {
        // 检查文件是否已存在，如果存在且不允许覆盖，则跳过
        File outputFile = new File(outputPath);
        if (outputFile.exists() && !generatorConfig.isOverwrite()) {
            System.out.println("文件已存在，跳过生成: " + outputPath);
            return;
        }
        
        // 创建目录
        FileUtils.createDirectory(outputFile.getParentFile());
        
        // 生成文件
        FreemarkerUtils.processTemplate(templateName, outputPath, dataModel);
        System.out.println("生成文件成功: " + outputPath);
    }
    
    public static void main(String[] args) {
        try {
            new CodeGenerator().generate();
            System.out.println("所有代码生成完成！");
        } catch (Exception e) {
            System.err.println("代码生成失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
